# main.py(如果修改了该文件内容一定要重启服务才能生效)
import os
import io
import jieba
import hashlib  # 导入hashlib模块，用于进行密码的哈希加密
import uvicorn  # 用来启用fastapi应用的库

import GCN
# import GCN.all_job
import models  # 导入ORM映射类
import schemas  # 导入Pydantic模型
import base64
from fastapi import FastAPI, UploadFile, File, Depends, HTTPException, Form  # 导入FastAPI框架和依赖项管理工具Depends
from sqlalchemy import select
from starlette.responses import JSONResponse
from Resume_to_Mysql import extract
from fastapi import FastAPI, Depends, HTTPException  # 导入FastAPI框架和依赖项管理工具Depends
from sqlalchemy.orm import Session  # 导入SQLAlchemy中的Session类，用于数据库会话管理
from fastapi.middleware.cors import CORSMiddleware
from database import SessionLocal  # 导入数据库会话实例
from GCN import mymodel, find_talents, all_job, assessment  # 导入推荐模型
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from fastapi.responses import StreamingResponse

# uvicorn main:app --reload 终端启用服务
app = FastAPI()  # 创建FastAPI应用实例
app.add_middleware(  # 添加 CORS 中间件
    CORSMiddleware,
    allow_origins=["*"],  # 允许所有来源的跨域请求
    allow_credentials=True,
    allow_methods=["*"],  # 允许所有 HTTP 方法
    allow_headers=["*"],  # 允许所有 HTTP 头部
)


def preload_jieba():
    # 加载 jieba 库的模型和其他资源
    jieba.initialize()


# Dependency
def get_db():
    db = SessionLocal()  # 获取数据库会话
    try:
        yield db  # 返回数据库会话
    finally:
        db.close()  # 关闭数据库会话


# 启动fastapi的应用程序
if __name__ == "__main__":
    uvicorn.run("main:app", host="127.0.0.1", port=8000, reload=True)
# 在应用启动时调用预加载函数
preload_jieba()

# 16字节的AES密钥
AES_KEY = b'fcxdcwjwcxwxtfjy'  # 请替换为自己的密钥，确保长度为16字节


# AES加密函数
def encrypt_aes(plaintext):
    # 生成随机 IV
    iv = os.urandom(16)
    cipher = AES.new(AES_KEY, AES.MODE_CBC, iv)
    ct_bytes = cipher.encrypt(pad(plaintext.encode(), AES.block_size))
    iv = base64.b64encode(iv).decode('utf-8')
    ct = base64.b64encode(ct_bytes).decode('utf-8')
    return iv, ct


# AES解密函数
def decrypt_aes(iv, ciphertext):
    iv = base64.b64decode(iv)
    ct = base64.b64decode(ciphertext)
    cipher = AES.new(AES_KEY, AES.MODE_CBC, iv)
    pt = unpad(cipher.decrypt(ct), AES.block_size)
    return pt.decode('utf-8')


# 求职者注册
@app.post("/register")  # 定义'/register'路径的POST请求处理函数
def register(user: schemas.UserRegister, db: Session = Depends(get_db)):
    # 查询数据库中是否存在相同的加密邮箱
    existing_users = db.query(models.Finder.Email, models.Finder.Email_iv).union_all(
        select(models.User.Email, models.User.Email_iv)
    ).all()  # union_all并集合并两个表
    for user_in_db in existing_users:
        decrypted_email = decrypt_aes(user_in_db.Email_iv, user_in_db.Email)
        if decrypted_email == user.Email:
            # 如果存在相同的解密邮箱，则说明邮箱已被注册
            raise HTTPException(status_code=400, detail="Email already registered")

    # 创建新用户
    # 使用AES进行哈希处理
    hashed_password = hashlib.md5(user.Password.encode()).hexdigest()
    iv_email, encrypted_email = encrypt_aes(user.Email)
    iv_tele, encrypted_tele = encrypt_aes(user.Tele)
    iv_address, encrypted_address = encrypt_aes(user.Address)

    db_user = models.User(
        Role=user.Role,
        Password=hashed_password,
        Email_iv=iv_email,
        Email=encrypted_email,
        Telephone_iv=iv_tele,
        Telephone=encrypted_tele,
        Address_iv=iv_address,
        Address=encrypted_address
    )
    db.add(db_user)
    db.commit()
    db.refresh(db_user)

    return {"message": "User registered successfully"}


# 招聘者注册
@app.post("/finder_register")  # 定义'/register'路径的POST请求处理函数
def register(user: schemas.UserRegister, db: Session = Depends(get_db)):
    # 查询数据库中是否存在相同的加密邮箱
    existing_users = db.query(models.Finder.Email, models.Finder.Email_iv).union_all(
        select(models.User.Email, models.User.Email_iv)
    ).all()
    for user_in_db in existing_users:
        decrypted_email = decrypt_aes(user_in_db.Email_iv, user_in_db.Email)
        if decrypted_email == user.Email:
            # 如果存在相同的解密邮箱，则说明邮箱已被注册
            raise HTTPException(status_code=400, detail="Email already registered")

    # 创建新用户
    # 使用AES进行哈希处理
    hashed_password = hashlib.md5(user.Password.encode()).hexdigest()
    iv_email, encrypted_email = encrypt_aes(user.Email)
    iv_tele, encrypted_tele = encrypt_aes(user.Tele)
    iv_address, encrypted_address = encrypt_aes(user.Address)

    db_user = models.Finder(
        Role=user.Role,
        Password=hashed_password,
        Email_iv=iv_email,
        Email=encrypted_email,
        Telephone_iv=iv_tele,
        Telephone=encrypted_tele,
        Address_iv=iv_address,
        Address=encrypted_address
    )
    db.add(db_user)
    db.commit()
    db.refresh(db_user)

    return {"message": "User registered successfully"}


# 建立接口： 在FastAPI应用程序中创建一个接口来接收注册表中的数据
# 发送请求： 在注册表的HTML代码中，使用JavaScript编写逻辑来获取表单数据，并将其作为POST请求发送到FastAPI接口
# 处理请求： 在FastAPI应用程序中，编写逻辑来接收和处理POST请求，并将表单数据存储到MySQL数据库中


def add_user(user, existing_users):
    for user_in_db in existing_users:
        decrypted_email = decrypt_aes(user_in_db.Email_iv, user_in_db.Email)
        if decrypted_email == user.Email:
            if hashlib.new('md5', user.Password.encode()).hexdigest() != user_in_db.Password:
                raise HTTPException(status_code=406, detail="wrong password")
            else:
                # 根据用户角色重定向到不同页面
                if user_in_db.Role == "jobSeeker" or user_in_db.Role == "recruiter":
                    redirect_url = "http://localhost:3000"  # 求职者,招聘者页面
                elif user_in_db.Role == 'root':
                    redirect_url = "http://localhost:3000/manage"  # 后台管理界面

                # 获取Telephone_iv和Address_iv
                email_iv = user_in_db.Email_iv
                user_role = user_in_db.Role

                # 解密Telephone和Address
                decrypted_email = decrypt_aes(email_iv, user_in_db.Email)

                return {"redirect_url": redirect_url, "decrypted_email": decrypted_email,
                        "user_role": user_role}


@app.post('/login')
def login(user: schemas.UserLogin, db: Session = Depends(get_db)):
    # 根据邮箱从数据库中查询用户
    existing_users1 = db.query(models.User).all()
    existing_users2 = db.query(models.Finder).all()
    if add_user(user, existing_users1):
        return add_user(user, existing_users1)
    return add_user(user, existing_users2)
    raise HTTPException(status_code=401, detail="User not found")


# 求职者上传简历并提取简历到数据库
@app.post("/upload")
async def upload_resume(file: UploadFile = File(...), user_email: str = Form(...), name: str = Form(...),
                        education_by_insert: str = Form(...), db: Session = Depends(get_db)):
    # 检查文件类型是否为允许的格式
    allowed_formats = [".pdf", ".jpg", ".jpeg", ".png", ".doc", ".docx", ".bmp", ".tif", ".tiff", ".gif"]
    if not any(file.filename.endswith(format) for format in allowed_formats):
        raise HTTPException(status_code=400, detail="只允许上传 PNG、PDF、DOC 和 DOCX 格式的文件。")

    contents = await file.read()

    current_dir = os.getcwd()

    # 构建到上级目录的路径
    parent_dir = os.path.dirname(current_dir)
    # 保存上传的文件到本地
    save_path = parent_dir+"\\Files\\Resume_upload\\" + file.filename    #名字推荐改为邮箱.png

    with open(save_path, "wb") as f:
        f.write(contents)

    # 将用户的电子邮件哈希化以便匹配数据库中的条目
    existing_users = db.query(models.User).all()
    for user_in_db in existing_users:
        decrypted_email = decrypt_aes(user_in_db.Email_iv, user_in_db.Email)
        if decrypted_email == user_email:
            try:
                # 调用提取函数，获取简历中的文本内容
                resume_n, education_by_shibie, resume_it = extract.extract_text_from_file(save_path)
                # resume_text = extract.extract_text_from_file(save_path)

                # 更新用户的 Resume_n、Education 和 Resume_it 字段
                # 上传的字符串如果大于255字节就在sql里用text类型
                # 上传的文件如果大于 65K就用MediumBlob类型
                # TinyBlob 最大 255
                # Blob 最大 65K
                # MediumBlob 最大16M
                # LongBlob 最大 4G

                # 原本学历部分打算这样写的，但是一直报错500
                # if education_by_shibie.equals('无学历'):
                #     user_in_db.Education = education_by_shibie
                # else:
                #     user_in_db.Education = education_by_insert

                user_in_db.Resume_n = resume_n
                user_in_db.Resume_it = resume_it
                user_in_db.Resume_file = contents
                user_in_db.Education = education_by_insert
                # user_in_db.Resume_text = resume_text
                user_in_db.Name = name

                db.commit()
            except Exception as e:
                db.rollback()
                raise HTTPException(status_code=500, detail="保存文件或更新用户信息失败")
            # 删除本地保存的文件
            os.remove(save_path)
            return "简历上传成功，并已提取文本内容并保存到数据库。"

    raise HTTPException(status_code=401, detail="用户不存在")


# 查找所有工作,不带条件
@app.post("/search_all_job_without_condition")
def search_resume():
    # 调用 GCN.all_job.get_information 函数，并传递转换后的列表
    search_Result = GCN.all_job.all_job_without_condition(1000)
    print(search_Result)
    # 如果找到用户，返回其简历信息
    return JSONResponse(content={"result": search_Result})


# 查找所有工作,带筛选条件
@app.post("/search_all_job_with_condition")
def search_resume(job: str = Form(...), salary: str = Form(...), education: str = Form(...), city: str = Form(...)):
    # 调用 GCN.all_job.get_information 函数，并传递转换后的列表
    search_Result = GCN.all_job.all_job_with_condition(job, salary, education, city, 1000)
    # 如果找到用户，返回其简历信息
    return JSONResponse(content={"result": search_Result})


# 为求职者匹配工作
@app.post("/search_job")
def search_resume(user_email: str = Form(...), db: Session = Depends(get_db)):
    existing_users = db.query(models.User).all()
    for user_in_db in existing_users:
        decrypted_email = decrypt_aes(user_in_db.Email_iv, user_in_db.Email)
        if decrypted_email == user_email:
            # 将 Resume_it 字符串转换为列表形式
            resume_it_list = user_in_db.Resume_it.split(',')
            # 调用 recommend_jobs_based_on_resume 函数，并传递转换后的列表
            search_Result = mymodel.recommend_jobs_based_on_resume(resume_it_list, user_in_db.Education)

            # 如果找到用户，返回其简历信息
            return JSONResponse(content={"result": search_Result})

    raise HTTPException(status_code=404, detail="用户不存在")


# 为招聘者匹配人才
@app.post("/search_talents")
def search_resume(user_email: str = Form(...), db: Session = Depends(get_db)):
    existing_users = db.query(models.Finder).all()
    for user_in_db in existing_users:
        decrypted_email = decrypt_aes(user_in_db.Email_iv, user_in_db.Email)
        if decrypted_email == user_email:
            title_require = user_in_db.Require_Discription.split(',')
            print('o')
            title_require_it = extract.extract_it_from_text(title_require)
            print('1')
            search_Result = find_talents.recommend_talents(title_require_it, user_in_db.Require_Education)
            print('2')
            return JSONResponse(content={"result": search_Result})

    raise HTTPException(status_code=404, detail="用户不存在")


# 通过填写的表格信息来提取和上传
@app.post("/upload_scanf")
async def upload_resume(user_email: str = Form(...), scanf: str = Form(...), db: Session = Depends(get_db)):
    # 将用户的电子邮件哈希化以便匹配数据库中的条目
    existing_users = db.query(models.User).all()
    for user_in_db in existing_users:
        decrypted_email = decrypt_aes(user_in_db.Email_iv, user_in_db.Email)
        if decrypted_email == user_email:
            try:
                resume_n, education_by_shibie, resume_it = extract.extract_text_from_text(scanf)
                user_in_db.Resume_n = resume_n
                user_in_db.Resume_it = resume_it
                user_in_db.Education = education_by_shibie

                db.commit()
            except Exception as e:
                db.rollback()
                raise HTTPException(status_code=500, detail="保存表格信息失败")
            return "填写表格信息上传成功，并已提取文本内容并保存到数据库。"

    raise HTTPException(status_code=401, detail="用户不存在")


# 通过填写的表格信息来上传招聘信息
@app.post("/upload_job")
async def upload_resume(user_email: str = Form(...), company: str = Form(...), company_address: str = Form(...),
                        requiew_title: str = Form(...), title_salary: str = Form(...),
                        require_education: str = Form(...),
                        require_discription: str = Form(...), db: Session = Depends(get_db)):
    # 将用户的电子邮件哈希化以便匹配数据库中的条目
    existing_users = db.query(models.Finder).all()
    for user_in_db in existing_users:
        decrypted_email = decrypt_aes(user_in_db.Email_iv, user_in_db.Email)
        if decrypted_email == user_email:
            try:
                user_in_db.Company = company
                user_in_db.Company_Address = company_address
                user_in_db.Require_Title = requiew_title
                user_in_db.Title_Salary = title_salary
                user_in_db.Require_Education = require_education
                user_in_db.Require_Discription = require_discription

                db.commit()
            except Exception as e:
                db.rollback()
                raise HTTPException(status_code=500, detail="招聘信息上传失败")
            return "招聘信息上传成功"

    raise HTTPException(status_code=401, detail="用户不存在")


# 能力评价返回岗位和雷达图数值
@app.post("/assessment")
async def upload_resume(user_email: str = Form(...), job: str = Form(...), db: Session = Depends(get_db)):
    # 将用户的电子邮件哈希化以便匹配数据库中的条目
    existing_users = db.query(models.User).all()
    print(0)
    for user_in_db in existing_users:
        decrypted_email = decrypt_aes(user_in_db.Email_iv, user_in_db.Email)
        if decrypted_email == user_email:
            try:
                resume_it = user_in_db.Resume_it
                resume_n = user_in_db.Resume_n
                education = user_in_db.Education

                jobs = GCN.assessment.assessment(resume_n, job, resume_it, education)


            except Exception as e:
                db.rollback()
                raise HTTPException(status_code=500, detail="保存表格信息失败",)
            return JSONResponse(content={"result": jobs})

    raise HTTPException(status_code=401, detail="用户不存在")

# 提取用户简历文件
# @app.get("/draw_file")
# async def get_user_resume(user_email: str, db: Session = SessionLocal()):
#     user = db.query(models.User).filter(models.User.Email == user_email).first()
#     if user:
#         resume_file = user.Resume_file
#         if resume_file:
#             # 将二进制数据写入临时文件
#             with io.BytesIO(resume_file) as stream:
#                 # 设置文件名
#                 filename = f"{user_email}_resume.pdf"  # 假设文件格式为 PDF
#                 # 返回文件流作为响应
#                 return StreamingResponse(stream, media_type='application/pdf', headers={'Content-Disposition': f'attachment; filename="{filename}"'})
#         else:
#             return {"message": "用户简历文件不存在"}
#     else:
#         return {"message": "用户不存在"}
